package cn.boodqian.airreport;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

import android.annotation.SuppressLint;
import android.text.format.DateUtils;

import com.google.gson.Gson;

import cn.boodqian.utils.Log;

@SuppressLint("DefaultLocale")
public class AirData {
	public int version = 0;
	private int length = 0;
	private Date time = null;
	private HashMap<String, ArrayList<Float>> data = new HashMap<String, ArrayList<Float>>();
	
	// Used by notification only
	public Date lastWarnTime = null;
	
	public static final long MS_PER_HOUR = 60*60*1000;
	public static final int MAX_HOURS = 24;//*30+24; // A month
	
	
	public void addData(String name, ArrayList<Float> data) {
		assert(name!=null && data!=null);
		if(length>0)
			assert(length==data.size());
		else
			length = data.size();
		this.data.put(name, data);
	}
	
	public int getLength() {
		return length;
	}
	
	public List<Float> getData(String name) {
		if(!data.containsKey(name)) return null;
		return  Collections.unmodifiableList(data.get(name));
	}
	public void setTime(Date t) {
		assert(t!=null);
		time = t;
	}
	public Date getTime() {
		return time;
	}
	
	public static int getCalendarDistance( Calendar cal1, Calendar cal2, int field ) {
	    long time1 = cal1.getTimeInMillis();
	    long time2 = cal2.getTimeInMillis();
	    int yearDistance = cal2.get(Calendar.YEAR) - cal1.get(Calendar.YEAR);
	    int rc = 0;
	    switch( field ) {
	    case Calendar.HOUR_OF_DAY:
	        rc = (int) ((time2 - time1)/DateUtils.HOUR_IN_MILLIS);
	        break;
	    case Calendar.DAY_OF_MONTH:
	        rc = (int) ((time2 - time1)/DateUtils.DAY_IN_MILLIS);
	        break;
	    case Calendar.MONTH:
	        int monthDistance = cal2.get(Calendar.MONTH) - cal1.get(Calendar.MONTH);
	        rc = yearDistance*12 + monthDistance;
	        break;
	    case Calendar.YEAR:
	        rc = yearDistance;
	        break;
	    }
	    return rc;
	}
	
	public void merge(AirData new_data, String timename) {
	    int field = GlobalData.gTimeTypeMap.get(timename);
	    int maxlength = GlobalData.gTimeLengthMap.get(timename);
		if(new_data==null || new_data.length == 0) return;
		
		Log.i("Begine merge " + timename);
		if(Log.isLoggable(Log.DEBUG)) Log.d("old data=" + GlobalData.gGson.toJson(this));
		if(Log.isLoggable(Log.DEBUG)) Log.d("new data=" + GlobalData.gGson.toJson(new_data));
		
		Calendar cal = Calendar.getInstance();
		cal.setTime(time);
		Calendar newCal = Calendar.getInstance();
		newCal.setTime(new_data.time);
		
		int distance = getCalendarDistance(cal, newCal, field);
		
		int currentMaxTime = 0;
		int currentMinTime =  currentMaxTime - (length-1);
		
		int maxTime = currentMaxTime + distance;
		int minTime =  maxTime - (new_data.length-1);
		
		if(Log.isLoggable(Log.DEBUG)) Log.d(String.format("%d-%d %d-%d", currentMinTime, currentMaxTime, minTime, maxTime));
		
		// Check having the same set of data
		assert(this.data.size()==new_data.data.size());
		for(String name:this.data.keySet()) {
			assert(new_data.data.containsKey(name));
		}
		
		// Expand the current list to include both time span
		if(maxTime > currentMaxTime) {
			for(long t = currentMaxTime+1;t<=maxTime;t++) {
				for(ArrayList<Float> list:data.values()) {
					list.add(0f);
				}
				this.length++;
			}
			currentMaxTime = maxTime;
		}
		else if(minTime < currentMinTime) {
			for(long t = minTime;t<currentMinTime;t++) {
				for(ArrayList<Float> list:data.values()) {
					list.add(0, 0f);
				}
				this.length++;
			}
			currentMinTime = minTime;
		}
		Calendar cal2 = (Calendar) cal.clone();
		cal2.add(field, currentMaxTime);
		time = cal2.getTime();
		if(Log.isLoggable(Log.DEBUG)) Log.d("merged data1=" + GlobalData.gGson.toJson(this));
		
		// Replace with new value if new value is not 0
		for(String name:this.data.keySet()) {
			//Log.i("replacing "+name);
		    if( new_data.getData(name) == null ) continue;
			for(int i=0;i<new_data.getLength();i++) {
				long t = minTime + i;
				int index = (int) (t - currentMinTime);

				if(new_data.getData(name).get(i).compareTo(0f) != 0) {
					Float v = (Float) new_data.getData(name).get(i);
					this.data.get(name).set(index, v);
				}
			}
		}
		
		if(Log.isLoggable(Log.DEBUG)) Log.d("merged data2=" + GlobalData.gGson.toJson(this));
		
		// Cut the data exceed the max capacity MAX_HOURS
		if(this.length > maxlength) {
			if(Log.isLoggable(Log.INFO)) Log.i(String.format("%d exceeds max capacity %d, cutting off", length, maxlength));
			for(String name:this.data.keySet()) {
				this.data.get(name).subList(0, length-maxlength).clear();
			}
			length = maxlength;
		}
		
		//Log.i("exit");
	}
}
